/********************************************************************
 *
 *      Filename: Vector3.cpp
 *   Description: See Vector3.h
 *        Author: [kb]ypp
 *    Created at: June 1, 2005
 * Last modified: October 22, 2005
 *
 ********************************************************************/

#include "Vector3.h"
#include "MathUtil.h"
#include "../core/Log.h"

const vec3 vec3::ZERO( 0.0, 0.0, 0.0 );

//! Does not perform any initalization
//!
vec3::vec3()
{}

vec3::vec3( const vec3 &v )
	: x( v.x ), y( v.y ), z( v.z )
{}

vec3::vec3( float nx, float ny, float nz )
	: x( nx ), y( ny ), z( nz )
{}

const vec3& vec3::operator=( const vec3 &rv )
{
	x = rv.x;
	y = rv.y;
	z = rv.z;
	return *this;
}

const vec3& vec3::operator+=( const vec3 &rv )
{
	x += rv.x;
	y += rv.y;
	z += rv.z;
	return *this;
}

const vec3& vec3::operator-=( const vec3 &rv )
{
	x -= rv.x;
	y -= rv.y;
	z -= rv.z;
	return *this;
}

const vec3 vec3::operator-() const // Unary "-"
{
	return vec3( -x, -y, -z );
}

// Add two vectors
const vec3 operator+( const vec3 &lv, const vec3 &rv )
{
	return vec3( lv.x + rv.x, lv.y + rv.y, lv.z + rv.z );
}

// Subtract
const vec3 operator-( const vec3 &lv, const vec3 &rv )
{
	return vec3( lv.x - rv.x, lv.y - rv.y, lv.z - rv.z );
}

const vec3 operator*( float k, const vec3 &v )
{
	return vec3( k * v.x, k * v.y, k * v.z );
}

const vec3 operator*( const vec3 &v, float k )
{
	return operator*( k, v );
}

bool operator==( const vec3 &lv, const vec3 &rv )
{
	return fabs( lv.x - rv.x ) < EPSILON &&
		   fabs( lv.y - rv.y ) < EPSILON &&
		   fabs( lv.z - rv.z ) < EPSILON;
}

bool operator!=( const vec3 &lv, const vec3 &rv )
{
	return !( lv == rv );
}

//! @f[ v = \frac{ v }{ ||v|| } @f]
//!
//! Produces a unit vector (or zero-vector + log warning)
void vec3::normalize()
{
	float len = length( *this );
	if ( len > EPSILON ) { // Avoid division-by-zero
		float oneToLen = 1.0 / len;
		x *= oneToLen;
		y *= oneToLen;
		z *= oneToLen;
	} else {
		LOG_WARNING( "Attempt to normalize() zero vector" );
		operator=( ZERO );
	}
}

//!
//! @f[ ||v|| @f]
float length( const vec3 &v )
{
	return sqrt( v.x * v.x + v.y * v.y + v.z * v.z );
}

//! @f[ ||v||^2 @f]
//!
//! Often handy. Computations are less costly than length() * length()
float lengthSq( const vec3 &v )
{
	return ( v.x * v.x + v.y * v.y + v.z * v.z );
}

//     | ax |      | bx |
// a = | ay |, b = | by |
//     | az |      | bz |
// ab = ax*bx + ay*by + az*bz
float dot( const vec3 &lv, const vec3 &rv )
{
	return ( lv.x * rv.x + lv.y * rv.y + lv.z * rv.z );
}

//     | ax |      | bx |
// a = | ay |, b = | by |
//     | az |      | bz |
//       |  ay*bz - by*az |   | ay*bz - az*by |
// axb = | -ax*bz + bx*az | = | az*bx - ax*bz |
//       |  ax*by - bx*ay |   | ax*by - ay*bx |
const vec3 cross( const vec3 &lv, const vec3 &rv )
{
	return vec3( lv.y * rv.z - lv.z * rv.y,
				 lv.z * rv.x - lv.x * rv.z,
				 lv.x * rv.y - lv.y * rv.x );
}
